if (APPLE OR SANITIZE STREQUAL "memory")
    # llvm-tblgen, that is used during LLVM build, will throw MSAN errors when running (breaking the build)
    # TODO: Retest when upgrading LLVM or build only llvm-tblgen without sanitizers
    set (ENABLE_EMBEDDED_COMPILER_DEFAULT OFF)
    set (ENABLE_DWARF_PARSER_DEFAULT OFF)
else()
    set (ENABLE_EMBEDDED_COMPILER_DEFAULT ${ENABLE_LIBRARIES})
    set (ENABLE_DWARF_PARSER_DEFAULT ${ENABLE_LIBRARIES})
endif()

option (ENABLE_EMBEDDED_COMPILER "Enable support for JIT compilation during query execution (uses LLVM library)" ${ENABLE_EMBEDDED_COMPILER_DEFAULT})

option (ENABLE_DWARF_PARSER "Enable support for DWARF input format (uses LLVM library)" ${ENABLE_DWARF_PARSER_DEFAULT})

option (ENABLE_BLAKE3 "Enable BLAKE3 function" ${ENABLE_LIBRARIES})

if (NOT ENABLE_EMBEDDED_COMPILER AND NOT ENABLE_DWARF_PARSER AND NOT ENABLE_BLAKE3)
    message(STATUS "Not using LLVM")
    return()
endif()

set (LLVM_VERSION "21.1.5bundled")
set (LLVM_INCLUDE_DIRS
    "${ClickHouse_SOURCE_DIR}/contrib/llvm-project/llvm/include"
    "${ClickHouse_BINARY_DIR}/contrib/llvm-project/llvm/include"
)
if (ENABLE_XRAY)
    set (LLVM_INCLUDE_DIRS "${LLVM_INCLUDE_DIRS}" "${ClickHouse_SOURCE_DIR}/contrib/llvm-project/compiler-rt/include")
endif ()

set (LLVM_LIBRARY_DIRS "${ClickHouse_BINARY_DIR}/contrib/llvm-project/llvm")

# There are compilation errors with C++23.
set (CMAKE_CXX_STANDARD 17)

if (ARCH_AMD64)
    set (LLVM_TARGETS_TO_BUILD "X86" CACHE INTERNAL "")
elseif (ARCH_AARCH64)
    set (LLVM_TARGETS_TO_BUILD "AArch64" CACHE INTERNAL "")
elseif (ARCH_PPC64LE)
    set (LLVM_TARGETS_TO_BUILD "PowerPC" CACHE INTERNAL "")
elseif (ARCH_S390X)
    set (LLVM_TARGETS_TO_BUILD "SystemZ" CACHE INTERNAL "")
elseif (ARCH_RISCV64)
    set (LLVM_TARGETS_TO_BUILD "RISCV" CACHE INTERNAL "")
endif ()

if (USE_MUSL)
    set (CMAKE_DL_LIBS "")
endif()

if (NOT ENABLE_EMBEDDED_COMPILER AND NOT ENABLE_DWARF_PARSER)
    # Only compiling blake3
    set (REQUIRED_LLVM_LIBRARIES LLVMSupport)
else()
    # This list was generated by listing all LLVM libraries, compiling the binary and removing all libraries while it still compiles.
    set (REQUIRED_LLVM_LIBRARIES
        LLVMExecutionEngine
        LLVMRuntimeDyld
        LLVMAsmPrinter
        LLVMDebugInfoDWARF
        LLVMGlobalISel
        LLVMSelectionDAG
        LLVMMCDisassembler
        LLVMPasses
        LLVMCodeGen
        LLVMipo
        LLVMBitWriter
        LLVMInstrumentation
        LLVMScalarOpts
        LLVMAggressiveInstCombine
        LLVMInstCombine
        LLVMVectorize
        LLVMTransformUtils
        LLVMTarget
        LLVMAnalysis
        LLVMProfileData
        LLVMObject
        LLVMBitReader
        LLVMCore
        LLVMRemarks
        LLVMBitstreamReader
        LLVMMCParser
        LLVMMC
        LLVMBinaryFormat
        LLVMDebugInfoCodeView
        LLVMSupport
        LLVMDemangle
    )

    if (ARCH_AMD64)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMX86Info LLVMX86Desc LLVMX86CodeGen)
    elseif (ARCH_AARCH64)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMAArch64Info LLVMAArch64Desc LLVMAArch64CodeGen)
    elseif (ARCH_PPC64LE)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMPowerPCInfo LLVMPowerPCDesc LLVMPowerPCCodeGen)
    elseif (ARCH_S390X)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMSystemZInfo LLVMSystemZDesc LLVMSystemZCodeGen)
    elseif (ARCH_RISCV64)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMRISCVInfo LLVMRISCVDesc LLVMRISCVCodeGen)
    endif ()

    if (ENABLE_XRAY)
        list(APPEND REQUIRED_LLVM_LIBRARIES LLVMXRay)
    endif ()
endif()


# Skip useless "install" instructions from CMake:
set (LLVM_INSTALL_TOOLCHAIN_ONLY 1 CACHE INTERNAL "")

message (STATUS "LLVM TARGETS TO BUILD ${LLVM_TARGETS_TO_BUILD}")

set (CMAKE_INSTALL_RPATH "ON") # Do not adjust RPATH in llvm, since then it will not be able to find libcxx/libcxxabi/libunwind
set (LLVM_COMPILER_CHECKED 1 CACHE INTERNAL "") # Skip internal compiler selection
set (LLVM_ENABLE_EH 1 CACHE INTERNAL "") # With exception handling
set (LLVM_ENABLE_RTTI 1 CACHE INTERNAL "")
set (LLVM_ENABLE_PIC 0 CACHE INTERNAL "")

# Omit unnecessary stuff (just the options which are ON by default)
set(LLVM_ENABLE_BACKTRACES 0 CACHE INTERNAL "")
set(LLVM_ENABLE_CRASH_OVERRIDES 0 CACHE INTERNAL "")
set(LLVM_ENABLE_TERMINFO 0 CACHE INTERNAL "")
set(LLVM_ENABLE_LIBXML2 0 CACHE INTERNAL "")
set(LLVM_ENABLE_LIBEDIT 0 CACHE INTERNAL "")
set(LLVM_ENABLE_LIBPFM 0 CACHE INTERNAL "")
set(LLVM_ENABLE_ZLIB 0 CACHE INTERNAL "")
set(LLVM_ENABLE_ZSTD 0 CACHE INTERNAL "")
set(LLVM_ENABLE_Z3_SOLVER 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_TOOLS 0 CACHE INTERNAL "")
set(LLVM_BUILD_TOOLS 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_UTILS 0 CACHE INTERNAL "")
set(LLVM_BUILD_UTILS 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_RUNTIMES 0 CACHE INTERNAL "")
set(LLVM_BUILD_RUNTIMES 0 CACHE INTERNAL "")
set(LLVM_BUILD_RUNTIME 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_EXAMPLES 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_TESTS 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_GO_TESTS 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_BENCHMARKS 0 CACHE INTERNAL "")
set(LLVM_INCLUDE_DOCS 0 CACHE INTERNAL "")
set(LLVM_ENABLE_OCAMLDOC 0 CACHE INTERNAL "")
set(LLVM_ENABLE_BINDINGS 0 CACHE INTERNAL "")

set (LLVM_SOURCE_DIR "${ClickHouse_SOURCE_DIR}/contrib/llvm-project/llvm")
set (LLVM_BINARY_DIR "${ClickHouse_BINARY_DIR}/contrib/llvm-project/llvm")

message (STATUS "LLVM CMAKE CROSS COMPILING ${CMAKE_CROSSCOMPILING}")
if (CMAKE_CROSSCOMPILING)
    set (LLVM_HOST_TRIPLE "${CMAKE_C_COMPILER_TARGET}" CACHE INTERNAL "")
    message (STATUS "CROSS COMPILING SET LLVM HOST TRIPLE ${LLVM_HOST_TRIPLE}")
endif()

# llvm-project/llvm/cmake/config-ix.cmake does a weird thing: it defines _LARGEFILE64_SOURCE,
# then checks if lseek64() function exists, then undefines _LARGEFILE64_SOURCE.
# Then the actual code that uses this function *doesn't* define _LARGEFILE64_SOURCE, so lseek64()
# may not exist and compilation fails. This happens with musl.
add_compile_definitions("_LARGEFILE64_SOURCE")

add_subdirectory ("${LLVM_SOURCE_DIR}" "${LLVM_BINARY_DIR}")

set_directory_properties (PROPERTIES
   # due to llvm crosscompile cmake does not know how to clean it, and on clean
   # will lead to the following error:
   #
   #   ninja: error: remove(contrib/llvm/llvm/NATIVE): Directory not empty
   #
   ADDITIONAL_CLEAN_FILES "${LLVM_BINARY_DIR}"
   # llvm's cmake configuring this file only when cmake runs,
   # and after clean cmake will not know that it should re-run,
   # add explicitly depends from llvm-config.h
   CMAKE_CONFIGURE_DEPENDS "${LLVM_BINARY_DIR}/include/llvm/Config/llvm-config.h"
)

add_library (_llvm INTERFACE)
target_link_libraries (_llvm INTERFACE ${REQUIRED_LLVM_LIBRARIES})
target_include_directories (_llvm SYSTEM BEFORE INTERFACE ${LLVM_INCLUDE_DIRS})
add_library(ch_contrib::llvm ALIAS _llvm)
